Attribute VB_Name = "modDirectDraw7"
Option Explicit
' **********************
' ** Renders graphics **
' **********************
' DirectDraw7 Object
Public DD As DirectDraw7
' Clipper object
Public DD_Clip As DirectDrawClipper

' primary surface
Public DDS_Primary As DirectDrawSurface7
Public DDSD_Primary As DDSURFACEDESC2

' back buffer
Public DDS_BackBuffer As DirectDrawSurface7
Public DDSD_BackBuffer As DDSURFACEDESC2

' Used for pre-rendering paperdolls
Public DDS_Player() As DirectDrawSurface7
Public DDSD_Player() As DDSURFACEDESC2

' gfx buffers
Public DDS_Item() As DirectDrawSurface7 ' arrays
Public DDS_Character() As DirectDrawSurface7
Public DDS_Paperdoll() As DirectDrawSurface7
Public DDS_Tileset() As DirectDrawSurface7
Public DDS_Resource() As DirectDrawSurface7
Public DDS_Animation() As DirectDrawSurface7
Public DDS_SpellIcon() As DirectDrawSurface7
Public DDS_Door As DirectDrawSurface7 ' singes
Public DDS_Blood As DirectDrawSurface7
Public DDS_Misc As DirectDrawSurface7
Public DDS_Direction As DirectDrawSurface7

' descriptions
Public DDSD_Temp As DDSURFACEDESC2 ' arrays
Public DDSD_Item() As DDSURFACEDESC2
Public DDSD_Character() As DDSURFACEDESC2
Public DDSD_Paperdoll() As DDSURFACEDESC2
Public DDSD_Tileset() As DDSURFACEDESC2
Public DDSD_Resource() As DDSURFACEDESC2
Public DDSD_Animation() As DDSURFACEDESC2
Public DDSD_SpellIcon() As DDSURFACEDESC2
Public DDSD_Door As DDSURFACEDESC2 ' singles
Public DDSD_Blood As DDSURFACEDESC2
Public DDSD_Misc As DDSURFACEDESC2
Public DDSD_Direction As DDSURFACEDESC2

' timers
Public Const SurfaceTimerMax As Long = 10000
Public CharacterTimer() As Long
Public PaperdollTimer() As Long
Public ItemTimer() As Long
Public ResourceTimer() As Long
Public AnimationTimer() As Long
Public SpellIconTimer() As Long

' Number of graphic files
Public NumTileSets As Long
Public NumCharacters As Long
Public NumPaperdolls As Long
Public NumItems As Long
Public NumResources As Long
Public NumAnimations As Long
Public NumSpellIcons As Long

' ********************
' ** Initialization **
' ********************
Public Function InitDirectDraw() As Boolean

    On Error GoTo ErrorHandle

    Call DestroyDirectDraw ' clear out everything
    ' Initialize direct draw
    Set DD = DX7.DirectDrawCreate(vbNullString) ' empty string forces primary device
    ' dictates how we access the screen and how other programs
    ' running at the same time will be allowed to access the screen as well.
    Call DD.SetCooperativeLevel(frmMainGame.hWnd, DDSCL_NORMAL)

    ' Init type and set the primary surface
    With DDSD_Primary
        .lFlags = DDSD_CAPS
        .ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE
    End With

    Set DDS_Primary = DD.CreateSurface(DDSD_Primary)
    ' Create the clipper
    Set DD_Clip = DD.CreateClipper(0)
    ' Associate the picture hwnd with the clipper
    Call DD_Clip.SetHWnd(frmMainGame.picScreen.hWnd)
    ' Have the blits to the screen clipped to the picture box
    Call DDS_Primary.SetClipper(DD_Clip) ' method attaches a clipper object to, or deletes one from, a surface.
    
    Call InitBackBuffer
    
    ' load persistent surfaces
    If FileExist(App.Path & "\data files\graphics\door.bmp", True) Then Call InitDDSurf("door", DDSD_Door, DDS_Door)
    If FileExist(App.Path & "\data files\graphics\blood.bmp", True) Then Call InitDDSurf("blood", DDSD_Blood, DDS_Blood)
    If FileExist(App.Path & "\data files\graphics\direction.bmp", True) Then Call InitDDSurf("direction", DDSD_Direction, DDS_Direction)
    
    InitDirectDraw = True
    
    Exit Function
ErrorHandle:

    Select Case Err.Number
        Case 91
            Call MsgBox("DirectX7 master object not created.")
    End Select

    InitDirectDraw = False
End Function

Private Sub InitBackBuffer()
    Dim rec As DxVBLib.RECT

    ' Initialize back buffer
    With DDSD_BackBuffer
        .lFlags = DDSD_CAPS Or DDSD_WIDTH Or DDSD_HEIGHT
        .ddsCaps.lCaps = DDSD_Temp.ddsCaps.lCaps
        .lWidth = (MAX_MAPX + 3) * PIC_X
        .lHeight = (MAX_MAPY + 3) * PIC_Y
    End With

    Set DDS_BackBuffer = DD.CreateSurface(DDSD_BackBuffer)
    Call DDS_BackBuffer.BltColorFill(rec, 0)
End Sub

' This sub gets the mask color from the surface loaded from a bitmap image
Public Sub SetMaskColorFromPixel(ByRef TheSurface As DirectDrawSurface7, ByVal X As Long, ByVal Y As Long)
    Dim TmpR As RECT
    Dim TmpDDSD As DDSURFACEDESC2
    Dim TmpColorKey As DDCOLORKEY

    With TmpR
        .Left = X
        .Top = Y
        .Right = X
        .Bottom = Y
    End With

    TheSurface.Lock TmpR, TmpDDSD, DDLOCK_WAIT Or DDLOCK_READONLY, 0

    With TmpColorKey
        .Low = TheSurface.GetLockedPixel(X, Y)
        .High = .Low
    End With

    TheSurface.SetColorKey DDCKEY_SRCBLT, TmpColorKey
    TheSurface.Unlock TmpR
End Sub

' Initializing a surface, using a bitmap
Public Sub InitDDSurf(FileName As String, ByRef SurfDesc As DDSURFACEDESC2, ByRef Surf As DirectDrawSurface7)

    On Error GoTo ErrorHandle

    ' Set path
    FileName = App.Path & GFX_PATH & FileName & GFX_EXT

    ' Destroy surface if it exist
    If Not Surf Is Nothing Then
        Set Surf = Nothing
        Call ZeroMemory(ByVal VarPtr(SurfDesc), LenB(SurfDesc))
    End If

    ' set flags
    SurfDesc.lFlags = DDSD_CAPS
    SurfDesc.ddsCaps.lCaps = DDSD_Temp.ddsCaps.lCaps
    ' init object
    Set Surf = DD.CreateSurfaceFromFile(FileName, SurfDesc)
    'Call Surf.SetColorKey(DDCKEY_SRCBLT, Key) ' MASK_COLOR
    Call SetMaskColorFromPixel(Surf, 0, 0)
    Exit Sub
ErrorHandle:

    Select Case Err.Number
            ' File not found
        Case 53
            MsgBox "missing file: " & FileName
            Call DestroyGame
            ' DirectDraw does not have enough memory to perform the operation.
        Case DDERR_OUTOFMEMORY
            MsgBox "Out of system memory"
            Call DestroyGame
            ' DirectDraw does not have enough display memory to perform the operation.
        Case DDERR_OUTOFVIDEOMEMORY
            MsgBox "Out of video memory, attempting to re-initialize using system memory"
            DDSD_Temp.lFlags = DDSD_CAPS
            DDSD_Temp.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN Or DDSCAPS_SYSTEMMEMORY
            Call ReInitDD
    End Select

End Sub

Public Function CheckSurfaces() As Boolean

    On Error GoTo ErrorHandle

    ' Check if we need to restore surfaces
    If NeedToRestoreSurfaces Then
        DD.RestoreAllSurfaces
    End If

    CheckSurfaces = True
    Exit Function
ErrorHandle:
    Call ReInitDD
    CheckSurfaces = False
End Function

Private Function NeedToRestoreSurfaces() As Boolean

    If Not DD.TestCooperativeLevel = DD_OK Then
        NeedToRestoreSurfaces = True
    End If

End Function

Public Sub ReInitDD()
    Call InitDirectDraw

    If InMapEditor Then
        Call InitDDSurf("misc", DDSD_Misc, DDS_Misc)
    End If

End Sub

Public Sub DestroyDirectDraw()
    Dim i As Long
    
    ' Unload DirectDraw
    Set DDS_Misc = Nothing
    
    For i = 1 To NumTileSets
        Set DDS_Tileset(i) = Nothing
        ZeroMemory ByVal VarPtr(DDSD_Tileset(i)), LenB(DDSD_Tileset(i))
    Next

    For i = 1 To NumItems
        Set DDS_Item(i) = Nothing
        ZeroMemory ByVal VarPtr(DDSD_Item(i)), LenB(DDSD_Item(i))
    Next

    For i = 1 To NumCharacters
        Set DDS_Character(i) = Nothing
        ZeroMemory ByVal VarPtr(DDSD_Character(i)), LenB(DDSD_Character(i))
    Next
    
    For i = 1 To NumPaperdolls
        Set DDS_Paperdoll(i) = Nothing
        ZeroMemory ByVal VarPtr(DDSD_Paperdoll(i)), LenB(DDSD_Paperdoll(i))
    Next
    
    For i = 1 To NumResources
        Set DDS_Resource(i) = Nothing
        ZeroMemory ByVal VarPtr(DDSD_Resource(i)), LenB(DDSD_Resource(i))
    Next
    
    For i = 1 To NumAnimations
        Set DDS_Animation(i) = Nothing
        ZeroMemory ByVal VarPtr(DDSD_Animation(i)), LenB(DDSD_Animation(i))
    Next
    
    For i = 1 To NumSpellIcons
        Set DDS_SpellIcon(i) = Nothing
        ZeroMemory ByVal VarPtr(DDSD_SpellIcon(i)), LenB(DDSD_SpellIcon(i))
    Next
    
    Set DDS_Blood = Nothing
    ZeroMemory ByVal VarPtr(DDSD_Blood), LenB(DDSD_Blood)
    
    Set DDS_Door = Nothing
    ZeroMemory ByVal VarPtr(DDSD_Door), LenB(DDSD_Door)
    
    Set DDS_Direction = Nothing
    ZeroMemory ByVal VarPtr(DDSD_Direction), LenB(DDSD_Direction)

    Set DDS_BackBuffer = Nothing
    Set DDS_Primary = Nothing
    Set DD_Clip = Nothing
    Set DD = Nothing
End Sub

' **************
' ** Blitting **
' **************
Public Sub Engine_BltFast(ByVal dx As Long, ByVal dy As Long, ByRef ddS As DirectDrawSurface7, srcRECT As RECT, trans As CONST_DDBLTFASTFLAGS)

    On Error GoTo ErrorHandle:

    If Not ddS Is Nothing Then
        Call DDS_BackBuffer.BltFast(dx, dy, ddS, srcRECT, trans)
    End If

    Exit Sub
ErrorHandle:

    Select Case Err.Number
        Case 5
            Call DevMsg("Attempting to copy from a surface thats not initialized.", BrightRed)
    End Select

End Sub

Public Function Engine_BltToDC(ByRef Surface As DirectDrawSurface7, sRECT As DxVBLib.RECT, dRECT As DxVBLib.RECT, ByRef picBox As VB.PictureBox, Optional Clear As Boolean = True) As Boolean

    On Error GoTo ErrorHandle

    If Clear Then
        picBox.Cls
    End If

    Call Surface.BltToDC(picBox.hDC, sRECT, dRECT)
    picBox.Refresh
    Engine_BltToDC = True
    Exit Function
ErrorHandle:
    ' returns false on error
    Engine_BltToDC = False
End Function

Public Sub BltDirection(ByVal X As Long, ByVal Y As Long)
    Dim rec As DxVBLib.RECT
    Dim i As Long
    
    ' render grid
    rec.Top = 24
    rec.Left = 0
    rec.Right = rec.Left + 32
    rec.Bottom = rec.Top + 32
    Call Engine_BltFast(ConvertMapX(X * PIC_X), ConvertMapY(Y * PIC_Y), DDS_Direction, rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
    
    ' render dir blobs
    For i = 1 To 4
        rec.Left = (i - 1) * 8
        rec.Right = rec.Left + 8
        ' find out whether render blocked or not
        If Not isDirBlocked(Map.Tile(X, Y).DirBlock, CByte(i)) Then
            rec.Top = 8
        Else
            rec.Top = 16
        End If
        rec.Bottom = rec.Top + 8
        'render!
        Call Engine_BltFast(ConvertMapX(X * PIC_X) + DirArrowX(i), ConvertMapY(Y * PIC_Y) + DirArrowY(i), DDS_Direction, rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
    Next
End Sub

Public Sub BltMapTile(ByVal X As Long, ByVal Y As Long)
    Dim rec As DxVBLib.RECT
    Dim i As Long
    
    With Map.Tile(X, Y)
        For i = MapLayer.Ground To MapLayer.Mask2
            ' skip tile if tileset isn't set
            If .Layer(i).tileset > 0 Then
                ' sort out rec
                rec.Top = .Layer(i).Y * PIC_Y
                rec.Bottom = rec.Top + PIC_Y
                rec.Left = .Layer(i).X * PIC_X
                rec.Right = rec.Left + PIC_X
                ' render
                Call Engine_BltFast(ConvertMapX(X * PIC_X), ConvertMapY(Y * PIC_Y), DDS_Tileset(.Layer(i).tileset), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
            End If
        Next
    End With
End Sub

Public Sub BltMapFringeTile(ByVal X As Long, ByVal Y As Long)
    Dim rec As DxVBLib.RECT
    Dim i As Long

    With Map.Tile(X, Y)
        For i = MapLayer.Fringe To MapLayer.Fringe2
            ' skip tile if tileset isn't set
            If .Layer(i).tileset > 0 Then
                ' sort out rec
                rec.Top = .Layer(i).Y * PIC_Y
                rec.Bottom = rec.Top + PIC_Y
                rec.Left = .Layer(i).X * PIC_X
                rec.Right = rec.Left + PIC_X
                ' render
                Call Engine_BltFast(ConvertMapX(X * PIC_X), ConvertMapY(Y * PIC_Y), DDS_Tileset(.Layer(i).tileset), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
            End If
        Next
    End With
End Sub

Public Sub BltDoor(ByVal X As Long, ByVal Y As Long)
    Dim rec As DxVBLib.RECT
    Dim x2 As Long, y2 As Long
    
    ' sort out animation
    With TempTile(X, Y)
        If .DoorAnimate = 1 Then ' opening
            If .DoorTimer + 100 < GetTickCount Then
                If .DoorFrame < 4 Then
                    .DoorFrame = .DoorFrame + 1
                Else
                    .DoorAnimate = 2 ' set to closing
                End If
                .DoorTimer = GetTickCount
            End If
        ElseIf .DoorAnimate = 2 Then ' closing
            If .DoorTimer + 100 < GetTickCount Then
                If .DoorFrame > 1 Then
                    .DoorFrame = .DoorFrame - 1
                Else
                    .DoorAnimate = 0 ' end animation
                End If
                .DoorTimer = GetTickCount
            End If
        End If
        
        If .DoorFrame = 0 Then .DoorFrame = 1
    End With

    With rec
        .Top = 0
        .Bottom = DDSD_Door.lHeight
        .Left = ((TempTile(X, Y).DoorFrame - 1) * (DDSD_Door.lWidth / 4))
        .Right = .Left + (DDSD_Door.lWidth / 4)
    End With

    x2 = (X * PIC_X)
    y2 = (Y * PIC_Y) - (DDSD_Door.lHeight / 2) + 4
    'Call Engine_BltFast(ConvertMapX(x2), ConvertMapY(y2), DDS_Door, rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
    Call DDS_BackBuffer.BltFast(ConvertMapX(x2), ConvertMapY(y2), DDS_Door, rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
End Sub

Public Sub BltBlood(ByVal Index As Long)
    Dim rec As DxVBLib.RECT
    
    With Blood(Index)
        ' check if we should be seeing it
        If .Timer + 20000 < GetTickCount Then Exit Sub
        
        rec.Top = 0
        rec.Bottom = PIC_Y
        rec.Left = (.Sprite - 1) * PIC_X
        rec.Right = rec.Left + PIC_X
        
        Engine_BltFast ConvertMapX(.X * PIC_X), ConvertMapY(.Y * PIC_Y), DDS_Blood, rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY
    End With
End Sub

Public Sub BltAnimation(ByVal Index As Long, ByVal Layer As Long)
    Dim Sprite As Integer
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    Dim i As Long
    Dim width As Long, height As Long
    Dim looptime As Long
    Dim FrameCount As Long
    Dim X As Long, Y As Long
    Dim lockindex As Long
    
    If AnimInstance(Index).Animation = 0 Then
        ClearAnimInstance Index
        Exit Sub
    End If
    
    Sprite = Animation(AnimInstance(Index).Animation).Sprite(Layer)
    
    If Sprite < 1 Or Sprite > NumAnimations Then Exit Sub
    
    FrameCount = Animation(AnimInstance(Index).Animation).Frames(Layer)
    
    AnimationTimer(Sprite) = GetTickCount + SurfaceTimerMax
    
    If DDS_Animation(Sprite) Is Nothing Then
        Call InitDDSurf("animations\" & Sprite, DDSD_Animation(Sprite), DDS_Animation(Sprite))
    End If
    
    ' total width divided by frame count
    width = DDSD_Animation(Sprite).lWidth / FrameCount
    height = DDSD_Animation(Sprite).lHeight
    
    sRECT.Top = 0
    sRECT.Bottom = height
    sRECT.Left = (AnimInstance(Index).FrameIndex(Layer) - 1) * width
    sRECT.Right = sRECT.Left + width
    
    ' change x or y if locked
    If AnimInstance(Index).LockType > TARGET_TYPE_NONE Then ' if <> none
        ' is a player
        If AnimInstance(Index).LockType = TARGET_TYPE_PLAYER Then
            ' quick save the index
            lockindex = AnimInstance(Index).lockindex
            ' check if is ingame
            If IsPlaying(lockindex) Then
                ' check if on same map
                If GetPlayerMap(lockindex) = GetPlayerMap(MyIndex) Then
                    ' is on map, is playing, set x & y
                    X = (GetPlayerX(lockindex) * PIC_X) + 16 - (width / 2) + Player(lockindex).XOffset
                    Y = (GetPlayerY(lockindex) * PIC_Y) + 16 - (height / 2) + Player(lockindex).YOffset
                End If
            End If
        ElseIf AnimInstance(Index).LockType = TARGET_TYPE_NPC Then
            ' quick save the index
            lockindex = AnimInstance(Index).lockindex
            ' check if NPC exists
            If MapNpc(lockindex).Num > 0 Then
                ' check if alive
                If MapNpc(lockindex).Vital(Vitals.HP) > 0 Then
                    ' exists, is alive, set x & y
                    X = (MapNpc(lockindex).X * PIC_X) + 16 - (width / 2) + MapNpc(lockindex).XOffset
                    Y = (MapNpc(lockindex).Y * PIC_Y) + 16 - (height / 2) + MapNpc(lockindex).YOffset
                Else
                    ' npc not alive anymore, kill the animation
                    ClearAnimInstance Index
                    Exit Sub
                End If
            Else
                ' npc not alive anymore, kill the animation
                ClearAnimInstance Index
                Exit Sub
            End If
        End If
    Else
        ' no lock, default x + y
        X = (AnimInstance(Index).X * 32) + 16 - (width / 2)
        Y = (AnimInstance(Index).Y * 32) + 16 - (height / 2)
    End If
    
    X = ConvertMapX(X)
    Y = ConvertMapY(Y)

    ' Clip to screen
    If Y < 0 Then

        With sRECT
            .Top = .Top - Y
        End With

        Y = 0
    End If

    If X < 0 Then

        With sRECT
            .Left = .Left - X
        End With

        X = 0
    End If

    If Y + height > DDSD_BackBuffer.lHeight Then
        sRECT.Bottom = sRECT.Bottom - (Y + height - DDSD_BackBuffer.lHeight)
    End If

    If X + width > DDSD_BackBuffer.lWidth Then
        sRECT.Right = sRECT.Right - (X + width - DDSD_BackBuffer.lWidth)
    End If
    
    Call Engine_BltFast(X, Y, DDS_Animation(Sprite), sRECT, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
End Sub

Public Sub BltItem(ByVal itemnum As Long)
    Dim PicNum As Integer
    Dim rec As DxVBLib.RECT
    Dim MaxFrames As Byte
    PicNum = Item(MapItem(itemnum).Num).Pic

    If PicNum < 1 Or PicNum > NumItems Then Exit Sub
    ItemTimer(PicNum) = GetTickCount + SurfaceTimerMax

    If DDS_Item(PicNum) Is Nothing Then
        Call InitDDSurf("items\" & PicNum, DDSD_Item(PicNum), DDS_Item(PicNum))
    End If

    If DDSD_Item(PicNum).lWidth > 64 Then ' has more than 1 frame

        With rec
            .Top = 0
            .Bottom = 32
            .Left = (MapItem(itemnum).Frame * 32)
            .Right = .Left + 32
        End With

    Else

        With rec
            .Top = 0
            .Bottom = PIC_Y
            .Left = 0
            .Right = PIC_X
        End With

    End If

    Call Engine_BltFast(ConvertMapX(MapItem(itemnum).X * PIC_X), ConvertMapY(MapItem(itemnum).Y * PIC_Y), DDS_Item(PicNum), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
End Sub

Public Sub BltMapResource(ByVal Resource_num As Long)
    Dim Resource_master As Long
    Dim Resource_state As Long
    Dim Resource_sprite As Long
    Dim rec As DxVBLib.RECT
    Dim X As Long, Y As Long
    ' Get the Resource type
    Resource_master = Map.Tile(MapResource(Resource_num).X, MapResource(Resource_num).Y).Data1
    
    If Resource_master = 0 Then Exit Sub

    If Resource(Resource_master).ResourceImage = 0 Then Exit Sub
    ' Get the Resource state
    Resource_state = MapResource(Resource_num).ResourceState

    If Resource_state = 0 Then ' normal
        Resource_sprite = Resource(Resource_master).ResourceImage
    ElseIf Resource_state = 1 Then ' used
        Resource_sprite = Resource(Resource_master).ExhaustedImage
    End If

    ' Load early
    If DDS_Resource(Resource_sprite) Is Nothing Then
        Call InitDDSurf("Resources\" & Resource_sprite, DDSD_Resource(Resource_sprite), DDS_Resource(Resource_sprite))
    End If

    ' src rect
    With rec
        .Top = 0
        .Bottom = DDSD_Resource(Resource_sprite).lHeight
        .Left = 0
        .Right = DDSD_Resource(Resource_sprite).lWidth
    End With

    ' Set base x + y, then the offset due to size
    X = (MapResource(Resource_num).X * PIC_X) - (DDSD_Resource(Resource_sprite).lWidth / 2) + 16
    Y = (MapResource(Resource_num).Y * PIC_Y) - DDSD_Resource(Resource_sprite).lHeight + 32
    Call BltResource(Resource_sprite, X, Y, rec)
End Sub

Private Sub BltResource(ByVal Resource As Long, ByVal dx As Long, dy As Long, rec As DxVBLib.RECT)

    If Resource < 1 Or Resource > NumResources Then Exit Sub
    Dim X As Long
    Dim Y As Long
    Dim width As Long
    Dim height As Long
    Dim destRECT As DxVBLib.RECT
    ResourceTimer(Resource) = GetTickCount + SurfaceTimerMax

    If DDS_Resource(Resource) Is Nothing Then
        Call InitDDSurf("Resources\" & Resource, DDSD_Resource(Resource), DDS_Resource(Resource))
    End If

    X = ConvertMapX(dx)
    Y = ConvertMapY(dy)
    width = (rec.Right - rec.Left)
    height = (rec.Bottom - rec.Top)

    ' Clip to screen
    If Y < 0 Then

        With rec
            .Top = .Top - Y
        End With

        Y = 0
    End If

    If X < 0 Then

        With rec
            .Left = .Left - X
        End With

        X = 0
    End If

    If Y + height > DDSD_BackBuffer.lHeight Then
        rec.Bottom = rec.Bottom - (Y + height - DDSD_BackBuffer.lHeight)
    End If

    If X + width > DDSD_BackBuffer.lWidth Then
        rec.Right = rec.Right - (X + width - DDSD_BackBuffer.lWidth)
    End If

    ' End clipping
    Call Engine_BltFast(X, Y, DDS_Resource(Resource), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
End Sub

Private Sub BltBars()
Dim tmpY As Long
Dim tmpX As Long
Dim barWidth As Long

    ' check for casting time bar
    If SpellBuffer > 0 Then
        ' lock to player
        tmpX = GetPlayerX(MyIndex) * PIC_X + Player(MyIndex).XOffset
        tmpY = GetPlayerY(MyIndex) * PIC_Y + Player(MyIndex).YOffset + 35
        ' calculate the width to fill
        barWidth = ((GetTickCount - SpellBufferTimer) / ((GetTickCount - SpellBufferTimer) + (Spell(PlayerSpells(SpellBuffer)).CastTime * 1000)) * 64)
        ' draw bars
        Call DDS_BackBuffer.SetFillColor(RGB(0, 0, 0))
        Call DDS_BackBuffer.DrawBox(ConvertMapX(tmpX), ConvertMapY(tmpY), ConvertMapX(tmpX + 32), ConvertMapY(tmpY + 4))
        Call DDS_BackBuffer.SetFillColor(QBColor(BrightCyan))
        Call DDS_BackBuffer.DrawBox(ConvertMapX(tmpX), ConvertMapY(tmpY), ConvertMapX(tmpX + barWidth), ConvertMapY(tmpY + 4))
    End If
End Sub

Public Sub BltPlayer(ByVal Index As Long)
    Dim anim As Byte
    Dim i As Long
    Dim X As Long
    Dim Y As Long
    Dim Sprite As Long, spriteleft As Long
    Dim rec As DxVBLib.RECT
    Dim attackspeed As Long
    Sprite = GetPlayerSprite(Index)

    If Sprite < 1 Or Sprite > NumCharacters Then Exit Sub

    ' speed from weapon
    If GetPlayerEquipment(Index, Weapon) > 0 Then
        attackspeed = Item(GetPlayerEquipment(Index, Weapon)).Speed
    Else
        attackspeed = 1000
    End If

    ' Reset frame
    anim = 1

    ' Check for attacking animation
    If Player(Index).AttackTimer + (attackspeed / 2) > GetTickCount Then
        If Player(Index).Attacking = 1 Then
            anim = 2
        End If
    Else
        ' If not attacking, walk normally
        Select Case GetPlayerDir(Index)
            Case DIR_UP

                If (Player(Index).YOffset > 8) Then anim = Player(Index).Step
            Case DIR_DOWN

                If (Player(Index).YOffset < -8) Then anim = Player(Index).Step
            Case DIR_LEFT

                If (Player(Index).XOffset > 8) Then anim = Player(Index).Step
            Case DIR_RIGHT

                If (Player(Index).XOffset < -8) Then anim = Player(Index).Step
        End Select

    End If

    ' Check to see if we want to stop making him attack
    With Player(Index)

        If .AttackTimer + attackspeed < GetTickCount Then
            .Attacking = 0
            .AttackTimer = 0
        End If

    End With

    ' Set the left
    Select Case GetPlayerDir(Index)
        Case DIR_UP
            spriteleft = 0
        Case DIR_RIGHT
            spriteleft = 1
        Case DIR_DOWN
            spriteleft = 2
        Case DIR_LEFT
            spriteleft = 3
    End Select

    With rec
        .Top = 0
        .Bottom = DDSD_Character(Sprite).lHeight
        .Left = (spriteleft * 3 + anim) * (DDSD_Character(Sprite).lWidth / 12)
        .Right = .Left + (DDSD_Character(Sprite).lWidth / 12)
    End With

    ' Calculate the X
    X = GetPlayerX(Index) * PIC_X + Player(Index).XOffset - ((DDSD_Character(Sprite).lWidth / 12 - 32) / 2)

    ' Is the player's height more than 32..?
    If (DDSD_Character(Sprite).lHeight) > 32 Then
        ' Create a 32 pixel offset for larger sprites
        Y = GetPlayerY(Index) * PIC_Y + Player(Index).YOffset - ((DDSD_Character(Sprite).lHeight) - 32)
    Else
        ' Proceed as normal
        Y = GetPlayerY(Index) * PIC_Y + Player(Index).YOffset
    End If

    ' render the actual sprite
    Call BltSprite(Sprite, X, Y, rec)
    
    ' check for paperdolling
    For i = 1 To Equipment.Equipment_Count - 1
        If GetPlayerEquipment(Index, i) > 0 Then
            If Item(GetPlayerEquipment(Index, i)).Paperdoll > 0 Then
                Call BltPaperdoll(X, Y, Item(GetPlayerEquipment(Index, i)).Paperdoll, anim, spriteleft)
            End If
        End If
    Next
End Sub

Public Sub BltNpc(ByVal MapNpcNum As Long)
    Dim anim As Byte
    Dim i As Long
    Dim X As Long
    Dim Y As Long
    Dim Sprite As Long, spriteleft As Long
    Dim rec As DxVBLib.RECT
    Dim attackspeed As Long
    
    If MapNpc(MapNpcNum).Num = 0 Then Exit Sub ' no npc set
    
    Sprite = Npc(MapNpc(MapNpcNum).Num).Sprite

    If Sprite < 1 Or Sprite > NumCharacters Then Exit Sub

    attackspeed = 1000

    ' Reset frame
    anim = 1

    ' Check for attacking animation
    If MapNpc(MapNpcNum).AttackTimer + (attackspeed / 2) > GetTickCount Then
        If MapNpc(MapNpcNum).Attacking = 1 Then
            anim = 2
        End If
    Else
        ' If not attacking, walk normally
        Select Case MapNpc(MapNpcNum).Dir
            Case DIR_UP
                If (MapNpc(MapNpcNum).YOffset > 8) Then anim = MapNpc(MapNpcNum).Step
            Case DIR_DOWN
                If (MapNpc(MapNpcNum).YOffset < -8) Then anim = MapNpc(MapNpcNum).Step
            Case DIR_LEFT
                If (MapNpc(MapNpcNum).XOffset > 8) Then anim = MapNpc(MapNpcNum).Step
            Case DIR_RIGHT
                If (MapNpc(MapNpcNum).XOffset < -8) Then anim = MapNpc(MapNpcNum).Step
        End Select
    End If

    ' Check to see if we want to stop making him attack
    With MapNpc(MapNpcNum)
        If .AttackTimer + attackspeed < GetTickCount Then
            .Attacking = 0
            .AttackTimer = 0
        End If
    End With

    ' Set the left
    Select Case MapNpc(MapNpcNum).Dir
        Case DIR_UP
            spriteleft = 0
        Case DIR_RIGHT
            spriteleft = 1
        Case DIR_DOWN
            spriteleft = 2
        Case DIR_LEFT
            spriteleft = 3
    End Select

    With rec
        .Top = 0
        .Bottom = DDSD_Character(Sprite).lHeight
        .Left = (spriteleft * 3 + anim) * (DDSD_Character(Sprite).lWidth / 12)
        .Right = .Left + (DDSD_Character(Sprite).lWidth / 12)
    End With

    ' Calculate the X
    X = MapNpc(MapNpcNum).X * PIC_X + MapNpc(MapNpcNum).XOffset - ((DDSD_Character(Sprite).lWidth / 12 - 32) / 2)

    ' Is the player's height more than 32..?
    If (DDSD_Character(Sprite).lHeight) > 32 Then
        ' Create a 32 pixel offset for larger sprites
        Y = MapNpc(MapNpcNum).Y * PIC_Y + MapNpc(MapNpcNum).YOffset - ((DDSD_Character(Sprite).lHeight) - 32)
    Else
        ' Proceed as normal
        Y = MapNpc(MapNpcNum).Y * PIC_Y + MapNpc(MapNpcNum).YOffset
    End If

    Call BltSprite(Sprite, X, Y, rec)
End Sub

Public Sub BltPaperdoll(ByVal x2 As Long, ByVal y2 As Long, ByVal Sprite As Long, ByVal anim As Long, ByVal spriteleft As Long)
    Dim rec As DxVBLib.RECT
    Dim X As Long, Y As Long
    Dim width As Long, height As Long
    
    If Sprite < 1 Or Sprite > NumPaperdolls Then Exit Sub
    
    If DDS_Paperdoll(Sprite) Is Nothing Then
        Call InitDDSurf("Paperdolls\" & Sprite, DDSD_Paperdoll(Sprite), DDS_Paperdoll(Sprite))
    End If
    
    With rec
        .Top = 0
        .Bottom = DDSD_Paperdoll(Sprite).lHeight
        .Left = (spriteleft * 3 + anim) * (DDSD_Paperdoll(Sprite).lWidth / 12)
        .Right = .Left + (DDSD_Paperdoll(Sprite).lWidth / 12)
    End With
    
    ' clipping
    X = ConvertMapX(x2)
    Y = ConvertMapY(y2)
    width = (rec.Right - rec.Left)
    height = (rec.Bottom - rec.Top)

    ' Clip to screen
    If Y < 0 Then
        With rec
            .Top = .Top - Y
        End With
        Y = 0
    End If

    If X < 0 Then
        With rec
            .Left = .Left - X
        End With
        X = 0
    End If

    If Y + height > DDSD_BackBuffer.lHeight Then
        rec.Bottom = rec.Bottom - (Y + height - DDSD_BackBuffer.lHeight)
    End If

    If X + width > DDSD_BackBuffer.lWidth Then
        rec.Right = rec.Right - (X + width - DDSD_BackBuffer.lWidth)
    End If
    ' /clipping
    
    Call Engine_BltFast(X, Y, DDS_Paperdoll(Sprite), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
End Sub

Private Sub BltSprite(ByVal Sprite As Long, ByVal x2 As Long, y2 As Long, rec As DxVBLib.RECT)
    If Sprite < 1 Or Sprite > NumCharacters Then Exit Sub
    Dim X As Long
    Dim Y As Long
    Dim width As Long
    Dim height As Long
    CharacterTimer(Sprite) = GetTickCount + SurfaceTimerMax

    If DDS_Character(Sprite) Is Nothing Then
        Call InitDDSurf("characters\" & Sprite, DDSD_Character(Sprite), DDS_Character(Sprite))
    End If

    X = ConvertMapX(x2)
    Y = ConvertMapY(y2)
    width = (rec.Right - rec.Left)
    height = (rec.Bottom - rec.Top)

    ' clipping
    If Y < 0 Then
        With rec
            .Top = .Top - Y
        End With
        Y = 0
    End If

    If X < 0 Then
        With rec
            .Left = .Left - X
        End With
        X = 0
    End If

    If Y + height > DDSD_BackBuffer.lHeight Then
        rec.Bottom = rec.Bottom - (Y + height - DDSD_BackBuffer.lHeight)
    End If

    If X + width > DDSD_BackBuffer.lWidth Then
        rec.Right = rec.Right - (X + width - DDSD_BackBuffer.lWidth)
    End If
    ' /clipping
    
    Call Engine_BltFast(X, Y, DDS_Character(Sprite), rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
End Sub

Sub BltAnimatedInvItems()
    Dim i As Long
    Dim itemnum As Long, itempic As Long
    Dim X As Long, Y As Long
    Dim MaxFrames As Byte
    Dim Amount As Long
    Dim rec As RECT, rec_pos As RECT

    If Not InGame Then Exit Sub

    ' check for map animation changes#
    For i = 1 To MAX_MAP_ITEMS

        If MapItem(i).Num > 0 Then
            itempic = Item(MapItem(i).Num).Pic

            If itempic < 1 Or itempic > NumItems Then Exit Sub
            MaxFrames = (DDSD_Item(itempic).lWidth / 2) / 32 ' Work out how many frames there are. /2 because of inventory icons as well as ingame

            If MapItem(i).Frame < MaxFrames - 1 Then
                MapItem(i).Frame = MapItem(i).Frame + 1
            Else
                MapItem(i).Frame = 1
            End If
        End If

    Next

    For i = 1 To MAX_INV
        itemnum = GetPlayerInvItemNum(MyIndex, i)

        If itemnum > 0 And itemnum <= MAX_ITEMS Then
            itempic = Item(itemnum).Pic

            If itempic > 0 And itempic <= NumItems Then
                If DDSD_Item(itempic).lWidth > 64 Then
                    MaxFrames = (DDSD_Item(itempic).lWidth / 2) / 32 ' Work out how many frames there are. /2 because of inventory icons as well as ingame

                    If InvItemFrame(i) < MaxFrames - 1 Then
                        InvItemFrame(i) = InvItemFrame(i) + 1
                    Else
                        InvItemFrame(i) = 1
                    End If

                    With rec
                        .Top = 0
                        .Bottom = 32
                        .Left = (DDSD_Item(itempic).lWidth / 2) + (InvItemFrame(i) * 32) ' middle to get the start of inv gfx, then +32 for each frame
                        .Right = .Left + 32
                    End With

                    With rec_pos
                        .Top = InvTop + ((InvOffsetY + 32) * ((i - 1) \ InvColumns))
                        .Bottom = .Top + PIC_Y
                        .Left = InvLeft + ((InvOffsetX + 32) * (((i - 1) Mod InvColumns)))
                        .Right = .Left + PIC_X
                    End With

                    ' Load item if not loaded, and reset timer
                    ItemTimer(itempic) = GetTickCount + SurfaceTimerMax

                    If DDS_Item(itempic) Is Nothing Then
                        Call InitDDSurf("Items\" & itempic, DDSD_Item(itempic), DDS_Item(itempic))
                    End If

                    ' We'll now re-blt the item, and place the currency value over it again :P
                    Engine_BltToDC DDS_Item(itempic), rec, rec_pos, frmMainGame.picInventory, False

                    ' If item is a stack - draw the amount you have
                    If GetPlayerInvItemValue(MyIndex, i) > 1 Then
                        Y = rec_pos.Top + 22
                        X = rec_pos.Left - 4
                        Amount = CStr(GetPlayerInvItemValue(MyIndex, i))
                        ' Draw currency but with k, m, b etc. using a convertion function
                        DrawText frmMainGame.picInventory.hDC, X, Y, ConvertCurrency(Amount), QBColor(Yellow)

                        ' Check if it's gold, and update the label
                        If GetPlayerInvItemNum(MyIndex, i) = 1 Then '1 = gold :P
                            frmMainGame.lblGold.Caption = Format$(Amount, "#,###,###,###")
                        End If
                    End If
                End If
            End If
        End If

    Next

    frmMainGame.picInventory.Refresh
End Sub

Sub BltEquipment()
    Dim i As Long, itemnum As Long, itempic As Long
    Dim rec As RECT, rec_pos As RECT
    
    If NumItems = 0 Then Exit Sub
    
    frmMainGame.picCharacter.Cls

    For i = 1 To Equipment.Equipment_Count - 1
        itemnum = GetPlayerEquipment(MyIndex, i)

        If itemnum > 0 Then
            itempic = Item(itemnum).Pic

            With rec
                .Top = 0
                .Bottom = 32
                .Left = 32
                .Right = 64
            End With

            With rec_pos
                .Top = EqTop
                .Bottom = .Top + PIC_Y
                .Left = EqLeft + ((EqOffsetX + 32) * (((i - 1) Mod EqColumns)))
                .Right = .Left + PIC_X
            End With

            ' Load item if not loaded, and reset timer
            ItemTimer(itempic) = GetTickCount + SurfaceTimerMax

            If DDS_Item(itempic) Is Nothing Then
                Call InitDDSurf("Items\" & itempic, DDSD_Item(itempic), DDS_Item(itempic))
            End If

            Engine_BltToDC DDS_Item(itempic), rec, rec_pos, frmMainGame.picCharacter, False
        End If

    Next

End Sub

Sub BltInventory()
    Dim i As Long, X As Long, Y As Long, itemnum As Long, itempic As Long
    Dim Amount As String
    Dim rec As RECT, rec_pos As RECT
    Dim colour As Long

    If Not InGame Then Exit Sub
    frmMainGame.picInventory.Cls

    For i = 1 To MAX_INV
        itemnum = GetPlayerInvItemNum(MyIndex, i)

        If itemnum > 0 And itemnum <= MAX_ITEMS Then
            itempic = Item(itemnum).Pic
            
            ' exit out if we're offering item in a trade.
            If InTrade > 0 Then
                For X = 1 To MAX_INV
                    If TradeYourOffer(X).Num = i Then
                        GoTo NextLoop
                    End If
                Next
            End If

            If itempic > 0 And itempic <= NumItems Then
                If DDSD_Item(itempic).lWidth <= 64 Then ' more than 1 frame is handled by anim sub

                    With rec
                        .Top = 0
                        .Bottom = 32
                        .Left = 32
                        .Right = 64
                    End With

                    With rec_pos
                        .Top = InvTop + ((InvOffsetY + 32) * ((i - 1) \ InvColumns))
                        .Bottom = .Top + PIC_Y
                        .Left = InvLeft + ((InvOffsetX + 32) * (((i - 1) Mod InvColumns)))
                        .Right = .Left + PIC_X
                    End With

                    ' Load item if not loaded, and reset timer
                    ItemTimer(itempic) = GetTickCount + SurfaceTimerMax

                    If DDS_Item(itempic) Is Nothing Then
                        Call InitDDSurf("Items\" & itempic, DDSD_Item(itempic), DDS_Item(itempic))
                    End If

                    Engine_BltToDC DDS_Item(itempic), rec, rec_pos, frmMainGame.picInventory, False

                    ' If item is a stack - draw the amount you have
                    If GetPlayerInvItemValue(MyIndex, i) > 1 Then
                        Y = rec_pos.Top + 22
                        X = rec_pos.Left - 4
                        Amount = CStr(GetPlayerInvItemValue(MyIndex, i))
                        
                        ' Draw currency but with k, m, b etc. using a convertion function
                        If CLng(Amount) < 1000000 Then
                            colour = QBColor(White)
                        ElseIf CLng(Amount) > 1000000 And CLng(Amount) < 10000000 Then
                            colour = QBColor(Yellow)
                        ElseIf CLng(Amount) > 10000000 Then
                            colour = QBColor(BrightGreen)
                        End If
                        
                        DrawText frmMainGame.picInventory.hDC, X, Y, ConvertCurrency(Amount), colour

                        ' Check if it's gold, and update the label
                        If GetPlayerInvItemNum(MyIndex, i) = 1 Then '1 = gold :P
                            frmMainGame.lblGold.Caption = Format$(Amount, "#,###,###,###")
                        End If
                    End If
                End If
            End If
        End If
NextLoop:
    Next

    'update animated items
    BltAnimatedInvItems
End Sub

Sub BltTrade()
    Dim i As Long, X As Long, Y As Long, itemnum As Long, itempic As Long
    Dim Amount As String
    Dim rec As RECT, rec_pos As RECT
    Dim colour As Long

    If Not InGame Then Exit Sub
    frmMainGame.picYourTrade.Cls
    frmMainGame.picTheirTrade.Cls
    
    For i = 1 To MAX_INV
        ' blt your own offer
        itemnum = GetPlayerInvItemNum(MyIndex, TradeYourOffer(i).Num)

        If itemnum > 0 And itemnum <= MAX_ITEMS Then
            itempic = Item(itemnum).Pic

            If itempic > 0 And itempic <= NumItems Then
                With rec
                    .Top = 0
                    .Bottom = 32
                    .Left = 32
                    .Right = 64
                End With

                With rec_pos
                    .Top = InvTop + ((InvOffsetY + 32) * ((i - 1) \ InvColumns))
                    .Bottom = .Top + PIC_Y
                    .Left = InvLeft + ((InvOffsetX + 32) * (((i - 1) Mod InvColumns)))
                    .Right = .Left + PIC_X
                End With

                ' Load item if not loaded, and reset timer
                ItemTimer(itempic) = GetTickCount + SurfaceTimerMax

                If DDS_Item(itempic) Is Nothing Then
                    Call InitDDSurf("Items\" & itempic, DDSD_Item(itempic), DDS_Item(itempic))
                End If

                Engine_BltToDC DDS_Item(itempic), rec, rec_pos, frmMainGame.picYourTrade, False

                ' If item is a stack - draw the amount you have
                If TradeYourOffer(i).Value > 1 Then
                    Y = rec_pos.Top + 22
                    X = rec_pos.Left - 4
                    
                    ' Draw currency but with k, m, b etc. using a convertion function
                    If Amount < 1000000 Then
                        colour = QBColor(White)
                    ElseIf Amount > 1000000 And CLng(Amount) < 10000000 Then
                        colour = QBColor(Yellow)
                    ElseIf Amount > 10000000 Then
                        colour = QBColor(BrightGreen)
                    End If
                    
                    Amount = CStr(TradeYourOffer(i).Value)
                    DrawText frmMainGame.picYourTrade.hDC, X, Y, ConvertCurrency(Amount), colour
                End If
            End If
        End If
            
        ' blt their offer
        itemnum = TradeTheirOffer(i).Num

        If itemnum > 0 And itemnum <= MAX_ITEMS Then
            itempic = Item(itemnum).Pic

            If itempic > 0 And itempic <= NumItems Then
                With rec
                    .Top = 0
                    .Bottom = 32
                    .Left = 32
                    .Right = 64
                End With

                With rec_pos
                    .Top = InvTop + ((InvOffsetY + 32) * ((i - 1) \ InvColumns))
                    .Bottom = .Top + PIC_Y
                    .Left = InvLeft + ((InvOffsetX + 32) * (((i - 1) Mod InvColumns)))
                    .Right = .Left + PIC_X
                End With

                ' Load item if not loaded, and reset timer
                ItemTimer(itempic) = GetTickCount + SurfaceTimerMax

                If DDS_Item(itempic) Is Nothing Then
                    Call InitDDSurf("Items\" & itempic, DDSD_Item(itempic), DDS_Item(itempic))
                End If

                Engine_BltToDC DDS_Item(itempic), rec, rec_pos, frmMainGame.picTheirTrade, False

                ' If item is a stack - draw the amount you have
                If TradeTheirOffer(i).Value > 1 Then
                    Y = rec_pos.Top + 22
                    X = rec_pos.Left - 4
                    
                    ' Draw currency but with k, m, b etc. using a convertion function
                    If Amount < 1000000 Then
                        colour = QBColor(White)
                    ElseIf Amount > 1000000 And CLng(Amount) < 10000000 Then
                        colour = QBColor(Yellow)
                    ElseIf Amount > 10000000 Then
                        colour = QBColor(BrightGreen)
                    End If
                    
                    Amount = CStr(TradeTheirOffer(i).Value)
                    DrawText frmMainGame.picTheirTrade.hDC, X, Y, ConvertCurrency(Amount), colour
                End If
            End If
        End If
    Next
End Sub

Sub BltPlayerSpells()
    Dim i As Long, X As Long, Y As Long, spellnum As Long, spellicon As Long
    Dim Amount As String
    Dim rec As RECT, rec_pos As RECT
    Dim colour As Long

    If Not InGame Then Exit Sub
    frmMainGame.picSpells.Cls

    For i = 1 To MAX_PLAYER_SPELLS
        spellnum = PlayerSpells(i)

        If spellnum > 0 And spellnum <= MAX_SPELLS Then
            spellicon = Spell(spellnum).Icon

            If spellicon > 0 And spellicon <= NumSpellIcons Then
            
                With rec
                    .Top = 0
                    .Bottom = 32
                    .Left = 0
                    .Right = 32
                End With
                
                If Not SpellCD(i) = 0 Then
                    rec.Left = 32
                    rec.Right = 64
                End If

                With rec_pos
                    .Top = SpellTop + ((SpellOffsetY + 32) * ((i - 1) \ SpellColumns))
                    .Bottom = .Top + PIC_Y
                    .Left = SpellLeft + ((SpellOffsetX + 32) * (((i - 1) Mod SpellColumns)))
                    .Right = .Left + PIC_X
                End With

                ' Load spellicon if not loaded, and reset timer
                SpellIconTimer(spellicon) = GetTickCount + SurfaceTimerMax

                If DDS_SpellIcon(spellicon) Is Nothing Then
                    Call InitDDSurf("SpellIcons\" & spellicon, DDSD_SpellIcon(spellicon), DDS_SpellIcon(spellicon))
                End If

                Engine_BltToDC DDS_SpellIcon(spellicon), rec, rec_pos, frmMainGame.picSpells, False
            End If
        End If
    Next
End Sub

Sub BltShop()
    Dim i As Long, X As Long, Y As Long, itemnum As Long, itempic As Long
    Dim Amount As String
    Dim rec As RECT, rec_pos As RECT
    Dim colour As Long

    If Not InGame Then Exit Sub
    frmMainGame.picShopItems.Cls

    For i = 1 To MAX_TRADES
        itemnum = Shop(InShop).TradeItem(i).Item 'GetPlayerInvItemNum(MyIndex, i)
        If itemnum > 0 And itemnum <= MAX_ITEMS Then
            itempic = Item(itemnum).Pic
            If itempic > 0 And itempic <= NumItems Then
            
                With rec
                    .Top = 0
                    .Bottom = 32
                    .Left = 32
                    .Right = 64
                End With
                
                With rec_pos
                    .Top = ShopTop + ((ShopOffsetY + 32) * ((i - 1) \ ShopColumns))
                    .Bottom = .Top + PIC_Y
                    .Left = ShopLeft + ((ShopOffsetX + 32) * (((i - 1) Mod ShopColumns)))
                    .Right = .Left + PIC_X
                End With
                
                ' Load item if not loaded, and reset timer
                ItemTimer(itempic) = GetTickCount + SurfaceTimerMax
                
                If DDS_Item(itempic) Is Nothing Then
                    Call InitDDSurf("Items\" & itempic, DDSD_Item(itempic), DDS_Item(itempic))
                End If
                
                Engine_BltToDC DDS_Item(itempic), rec, rec_pos, frmMainGame.picShopItems, False
                
                ' If item is a stack - draw the amount you have
                If Shop(InShop).TradeItem(i).ItemValue > 1 Then
                    Y = rec_pos.Top + 22
                    X = rec_pos.Left - 4
                    Amount = CStr(Shop(InShop).TradeItem(i).ItemValue)
                    
                    ' Draw currency but with k, m, b etc. using a convertion function
                    If CLng(Amount) < 1000000 Then
                        colour = QBColor(White)
                    ElseIf CLng(Amount) > 1000000 And CLng(Amount) < 10000000 Then
                        colour = QBColor(Yellow)
                    ElseIf CLng(Amount) > 10000000 Then
                        colour = QBColor(BrightGreen)
                    End If
                    
                    DrawText frmMainGame.picShopItems.hDC, X, Y, ConvertCurrency(Amount), colour
                End If
            End If
        End If
    Next
End Sub

Public Sub BltInventoryItem(ByVal X As Long, ByVal Y As Long)
    Dim rec As RECT, rec_pos As RECT
    Dim itemnum As Long, itempic As Long
    itemnum = GetPlayerInvItemNum(MyIndex, DragInvSlotNum)

    If itemnum > 0 And itemnum <= MAX_ITEMS Then
        itempic = Item(itemnum).Pic
        
        If itempic = 0 Then Exit Sub

        With rec
            .Top = 0
            .Bottom = .Top + PIC_Y
            .Left = DDSD_Item(itempic).lWidth / 2
            .Right = .Left + PIC_X
        End With

        With rec_pos
            .Top = 0
            .Bottom = .Top + PIC_Y
            .Left = 0
            .Right = .Left + PIC_X
        End With

        ' Load item if not loaded, and reset timer
        ItemTimer(itempic) = GetTickCount + SurfaceTimerMax

        If DDS_Item(itempic) Is Nothing Then
            Call InitDDSurf("Items\" & itempic, DDSD_Item(itempic), DDS_Item(itempic))
        End If

        Engine_BltToDC DDS_Item(itempic), rec, rec_pos, frmMainGame.picTempInv, False

        With frmMainGame.picTempInv
            .Top = Y
            .Left = X
            .Visible = True
            .ZOrder (0)
        End With

    End If

End Sub

' ******************
' ** Game Editors **
' ******************
Public Sub EditorMap_BltTileset()
    Dim height As Long
    Dim width As Long
    Dim tileset As Byte
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    
    ' find tileset number
    tileset = frmEditor_Map.scrlTileSet.Value
    
    ' exit out if doesn't exist
    If tileset < 0 Or tileset > NumTileSets Then Exit Sub
    
    ' make sure it's loaded
    If DDS_Tileset(tileset) Is Nothing Then
        Call InitDDSurf("tilesets\" & tileset, DDSD_Tileset(tileset), DDS_Tileset(tileset))
    End If
    
    height = DDSD_Tileset(tileset).lHeight
    width = DDSD_Tileset(tileset).lWidth
    
    dRECT.Top = 0
    dRECT.Bottom = height
    dRECT.Left = 0
    dRECT.Right = width
    
    frmEditor_Map.picBackSelect.height = height
    frmEditor_Map.picBackSelect.width = width
    
    Call Engine_BltToDC(DDS_Tileset(tileset), sRECT, dRECT, frmEditor_Map.picBackSelect)
End Sub

Public Sub BltTileOutline()
    Dim rec As DxVBLib.RECT
    
    If frmEditor_Map.optBlock.Value Then Exit Sub

    With rec
        .Top = 0
        .Bottom = .Top + PIC_Y
        .Left = 0
        .Right = .Left + PIC_X
    End With

    Call Engine_BltFast(ConvertMapX(CurX * PIC_X), ConvertMapY(CurY * PIC_Y), DDS_Misc, rec, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
End Sub

Public Sub NewCharacterBltSprite()
    Dim Sprite As Long
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    Dim width As Long, height As Long
    
    If frmMenu.cmbClass.ListIndex = -1 Then Exit Sub
    
    If newCharClass = 0 Then
        Sprite = Class(frmMenu.cmbClass.ListIndex + 1).MaleSprite(newCharSprite)
    Else
        Sprite = Class(frmMenu.cmbClass.ListIndex + 1).FemaleSprite(newCharSprite)
    End If
    
    If Sprite < 1 Or Sprite > NumCharacters Then
        frmMenu.picSprite.Cls
        Exit Sub
    End If
    
    CharacterTimer(Sprite) = GetTickCount + SurfaceTimerMax

    If DDS_Character(Sprite) Is Nothing Then
        Call InitDDSurf("Characters\" & Sprite, DDSD_Character(Sprite), DDS_Character(Sprite))
    End If
    
    width = DDSD_Character(Sprite).lWidth / 12
    height = DDSD_Character(Sprite).lHeight
    
    frmMenu.picSprite.width = width
    frmMenu.picSprite.height = height
    
    sRECT.Top = 0
    sRECT.Bottom = height
    sRECT.Left = width * 7 'looking down
    sRECT.Right = sRECT.Left + width
    
    dRECT.Top = 0
    dRECT.Bottom = height
    dRECT.Left = 0
    dRECT.Right = width
    
    Call Engine_BltToDC(DDS_Character(Sprite), sRECT, dRECT, frmMenu.picSprite)
End Sub

Public Sub EditorMap_BltMapItem()
    Dim itemnum As Integer
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    itemnum = Item(frmEditor_Map.scrlMapItem.Value).Pic

    If itemnum < 1 Or itemnum > NumItems Then
        frmEditor_Map.picMapItem.Cls
        Exit Sub
    End If

    ItemTimer(itemnum) = GetTickCount + SurfaceTimerMax

    If DDS_Item(itemnum) Is Nothing Then
        Call InitDDSurf("Items\" & itemnum, DDSD_Item(itemnum), DDS_Item(itemnum))
    End If

    sRECT.Top = 0
    sRECT.Bottom = PIC_Y
    sRECT.Left = 0
    sRECT.Right = PIC_X
    dRECT.Top = 0
    dRECT.Bottom = PIC_Y
    dRECT.Left = 0
    dRECT.Right = PIC_X
    Call Engine_BltToDC(DDS_Item(itemnum), sRECT, dRECT, frmEditor_Map.picMapItem)
End Sub

Public Sub EditorMap_BltKey()
    Dim itemnum As Integer
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    itemnum = Item(frmEditor_Map.scrlMapKey.Value).Pic

    If itemnum < 1 Or itemnum > NumItems Then
        frmEditor_Map.picMapKey.Cls
        Exit Sub
    End If

    ItemTimer(itemnum) = GetTickCount + SurfaceTimerMax

    If DDS_Item(itemnum) Is Nothing Then
        Call InitDDSurf("Items\" & itemnum, DDSD_Item(itemnum), DDS_Item(itemnum))
    End If

    sRECT.Top = 0
    sRECT.Bottom = PIC_Y
    sRECT.Left = 0
    sRECT.Right = PIC_X
    dRECT.Top = 0
    dRECT.Bottom = PIC_Y
    dRECT.Left = 0
    dRECT.Right = PIC_X
    Call Engine_BltToDC(DDS_Item(itemnum), sRECT, dRECT, frmEditor_Map.picMapKey)
End Sub

Public Sub EditorItem_BltItem()
    Dim itemnum As Integer
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    itemnum = frmEditor_Item.scrlPic.Value

    If itemnum < 1 Or itemnum > NumItems Then
        frmEditor_Item.picItem.Cls
        Exit Sub
    End If

    ItemTimer(itemnum) = GetTickCount + SurfaceTimerMax

    If DDS_Item(itemnum) Is Nothing Then
        Call InitDDSurf("Items\" & itemnum, DDSD_Item(itemnum), DDS_Item(itemnum))
    End If

    ' rect for source
    sRECT.Top = 0
    sRECT.Bottom = PIC_Y
    sRECT.Left = 0
    sRECT.Right = PIC_X
    ' same for destination as source
    dRECT = sRECT
    Call Engine_BltToDC(DDS_Item(itemnum), sRECT, dRECT, frmEditor_Item.picItem)
End Sub

Public Sub EditorItem_BltPaperdoll()
    Dim Sprite As Long
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    
    frmEditor_Item.picPaperdoll.Cls
    
    Sprite = frmEditor_Item.scrlPaperdoll.Value

    If Sprite < 1 Or Sprite > NumPaperdolls Then
        frmEditor_Item.picItem.Cls
        Exit Sub
    End If

    PaperdollTimer(Sprite) = GetTickCount + SurfaceTimerMax

    If DDS_Paperdoll(Sprite) Is Nothing Then
        Call InitDDSurf("paperdolls\" & Sprite, DDSD_Paperdoll(Sprite), DDS_Paperdoll(Sprite))
    End If

    ' rect for source
    sRECT.Top = 0
    sRECT.Bottom = DDSD_Paperdoll(Sprite).lHeight
    sRECT.Left = 0
    sRECT.Right = DDSD_Paperdoll(Sprite).lWidth
    ' same for destination as source
    dRECT = sRECT
    
    Call Engine_BltToDC(DDS_Paperdoll(Sprite), sRECT, dRECT, frmEditor_Item.picPaperdoll)
End Sub

Public Sub EditorSpell_BltIcon()
    Dim iconnum As Integer
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    iconnum = frmEditor_Spell.scrlIcon.Value
    
    If iconnum < 1 Or iconnum > NumSpellIcons Then
        frmEditor_Spell.picSprite.Cls
        Exit Sub
    End If
    
    SpellIconTimer(iconnum) = GetTickCount + SurfaceTimerMax
    
    If DDS_SpellIcon(iconnum) Is Nothing Then
        Call InitDDSurf("SpellIcons\" & iconnum, DDSD_SpellIcon(iconnum), DDS_SpellIcon(iconnum))
    End If
    
    sRECT.Top = 0
    sRECT.Bottom = PIC_Y
    sRECT.Left = 0
    sRECT.Right = PIC_X
    dRECT.Top = 0
    dRECT.Bottom = PIC_Y
    dRECT.Left = 0
    dRECT.Right = PIC_X
    
    Call Engine_BltToDC(DDS_SpellIcon(iconnum), sRECT, dRECT, frmEditor_Spell.picSprite)
End Sub

Public Sub EditorAnim_BltAnim()
    Dim Animationnum As Integer
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    Dim i As Long
    Dim width As Long, height As Long
    Dim looptime As Long
    Dim FrameCount As Long
    Dim ShouldRender As Boolean
    
    For i = 0 To 1
        Animationnum = frmEditor_Animation.scrlSprite(i).Value
        
        If Animationnum < 1 Or Animationnum > NumAnimations Then
            frmEditor_Animation.picSprite(i).Cls
        Else
            looptime = frmEditor_Animation.scrlLoopTime(i)
            FrameCount = frmEditor_Animation.scrlFrameCount(i)
            
            ShouldRender = False
            
            ' check if we need to render new frame
            If AnimEditorTimer(i) + looptime <= GetTickCount Then
                ' check if out of range
                If AnimEditorFrame(i) >= FrameCount Then
                    AnimEditorFrame(i) = 1
                Else
                    AnimEditorFrame(i) = AnimEditorFrame(i) + 1
                End If
                AnimEditorTimer(i) = GetTickCount
                ShouldRender = True
            End If
        
            If ShouldRender Then
                frmEditor_Animation.picSprite(i).Cls
            
                AnimationTimer(Animationnum) = GetTickCount + SurfaceTimerMax
                
                If DDS_Animation(Animationnum) Is Nothing Then
                    Call InitDDSurf("animations\" & Animationnum, DDSD_Animation(Animationnum), DDS_Animation(Animationnum))
                End If
                
                If frmEditor_Animation.scrlFrameCount(i).Value > 0 Then
                    ' total width divided by frame count
                    width = DDSD_Animation(Animationnum).lWidth / frmEditor_Animation.scrlFrameCount(i).Value
                    height = DDSD_Animation(Animationnum).lHeight
                    
                    sRECT.Top = 0
                    sRECT.Bottom = height
                    sRECT.Left = (AnimEditorFrame(i) - 1) * width
                    sRECT.Right = sRECT.Left + width
                    
                    dRECT.Top = 0
                    dRECT.Bottom = height
                    dRECT.Left = 0
                    dRECT.Right = width
                    
                    Call Engine_BltToDC(DDS_Animation(Animationnum), sRECT, dRECT, frmEditor_Animation.picSprite(i))
                End If
            End If
        End If
    Next
End Sub

Public Sub EditorNpc_BltSprite()
    Dim Sprite As Long
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    Sprite = frmEditor_NPC.scrlSprite.Value

    If Sprite < 1 Or Sprite > NumCharacters Then
        frmEditor_NPC.picSprite.Cls
        Exit Sub
    End If

    CharacterTimer(Sprite) = GetTickCount + SurfaceTimerMax

    If DDS_Character(Sprite) Is Nothing Then
        Call InitDDSurf("characters\" & Sprite, DDSD_Character(Sprite), DDS_Character(Sprite))
    End If

    sRECT.Top = 0
    sRECT.Bottom = SIZE_Y
    sRECT.Left = PIC_X * 3 ' facing down
    sRECT.Right = sRECT.Left + SIZE_X
    dRECT.Top = 0
    dRECT.Bottom = SIZE_Y
    dRECT.Left = 0
    dRECT.Right = SIZE_X
    Call Engine_BltToDC(DDS_Character(Sprite), sRECT, dRECT, frmEditor_NPC.picSprite)
End Sub

Public Sub EditorResource_BltSprite()
    Dim Sprite As Long
    Dim sRECT As DxVBLib.RECT
    Dim dRECT As DxVBLib.RECT
    
    ' normal sprite
    Sprite = frmEditor_Resource.scrlNormalPic.Value

    If Sprite < 1 Or Sprite > NumResources Then
        frmEditor_Resource.picNormalPic.Cls
    Else
        ResourceTimer(Sprite) = GetTickCount + SurfaceTimerMax
        If DDS_Resource(Sprite) Is Nothing Then
            Call InitDDSurf("Resources\" & Sprite, DDSD_Resource(Sprite), DDS_Resource(Sprite))
        End If
        sRECT.Top = 0
        sRECT.Bottom = DDSD_Resource(Sprite).lHeight
        sRECT.Left = 0
        sRECT.Right = DDSD_Resource(Sprite).lWidth
        dRECT.Top = 0
        dRECT.Bottom = DDSD_Resource(Sprite).lHeight
        dRECT.Left = 0
        dRECT.Right = DDSD_Resource(Sprite).lWidth
        Call Engine_BltToDC(DDS_Resource(Sprite), sRECT, dRECT, frmEditor_Resource.picNormalPic)
    End If

    ' exhausted sprite
    Sprite = frmEditor_Resource.scrlExhaustedPic.Value

    If Sprite < 1 Or Sprite > NumResources Then
        frmEditor_Resource.picExhaustedPic.Cls
    Else
        ResourceTimer(Sprite) = GetTickCount + SurfaceTimerMax
        If DDS_Resource(Sprite) Is Nothing Then
            Call InitDDSurf("Resources\" & Sprite, DDSD_Resource(Sprite), DDS_Resource(Sprite))
        End If
        sRECT.Top = 0
        sRECT.Bottom = DDSD_Resource(Sprite).lHeight
        sRECT.Left = 0
        sRECT.Right = DDSD_Resource(Sprite).lWidth
        dRECT.Top = 0
        dRECT.Bottom = DDSD_Resource(Sprite).lHeight
        dRECT.Left = 0
        dRECT.Right = DDSD_Resource(Sprite).lWidth
        Call Engine_BltToDC(DDS_Resource(Sprite), sRECT, dRECT, frmEditor_Resource.picExhaustedPic)
    End If
End Sub

Public Sub Render_Graphics()
    'On Error GoTo ErrorHandle
    Dim X As Long
    Dim Y As Long
    Dim i As Long
    Dim rec As DxVBLib.RECT
    Dim rec_pos As DxVBLib.RECT
    
    ' don't render
    If Not CheckSurfaces Then Exit Sub
    ' don't render
    If frmMainGame.WindowState = vbMinimized Then Exit Sub
    If GettingMap Then Exit Sub
    
    UpdateCamera
    
    ' update animation editor
    If Editor = EDITOR_ANIMATION Then
        EditorAnim_BltAnim
    End If
    
    DDS_BackBuffer.BltColorFill rec_pos, 0

    ' blit lower tiles
    If NumTileSets > 0 Then
        For X = TileView.Left To TileView.Right
            For Y = TileView.Top To TileView.Bottom
                If IsValidMapPoint(X, Y) Then
                    Call BltMapTile(X, Y)
                End If
            Next
        Next
    End If
    
    For i = 1 To MAX_BYTE
        Call BltBlood(i)
    Next

    ' Blit out the items
    If NumItems > 0 Then
        For i = 1 To MAX_MAP_ITEMS
    
            If MapItem(i).Num > 0 Then
                Call BltItem(i)
            End If
    
        Next
    End If
    
    'Draw sum d00rs.
    For X = TileView.Left To TileView.Right
        For Y = TileView.Top To TileView.Bottom

            If IsValidMapPoint(X, Y) Then
                If Map.Tile(X, Y).Type = TILE_TYPE_DOOR Then
                    BltDoor X, Y
                End If
            End If

        Next
    Next
    
    ' draw animations
    If NumAnimations > 0 Then
        For i = 1 To MAX_BYTE
            If AnimInstance(i).Used(0) Then
                BltAnimation i, 0
            End If
        Next
    End If

    ' Y-based render. Renders Players, Npcs and Resources based on Y-axis.
    For Y = 0 To Map.MaxY

        If NumCharacters > 0 Then
            ' Players
            For i = 1 To MAX_PLAYERS
                If IsPlaying(i) And GetPlayerMap(i) = GetPlayerMap(MyIndex) Then
                    If Player(i).Y = Y Then
                        Call BltPlayer(i)
                    End If
                End If
            Next
        
            ' Npcs
            For i = 1 To MAX_MAP_NPCS
                If MapNpc(i).Y = Y Then
                    Call BltNpc(i)
                End If
            Next
        End If
        
        ' Resources
        If NumResources > 0 Then
            If Resources_Init Then
                If Resource_Index > 0 Then
                    For i = 1 To Resource_Index
                        If MapResource(i).Y = Y Then
                            Call BltMapResource(i)
                        End If
                    Next
                End If
            End If
        End If
    Next
    
    ' animations
    If NumAnimations > 0 Then
        For i = 1 To MAX_BYTE
            If AnimInstance(i).Used(1) Then
                BltAnimation i, 1
            End If
        Next
    End If

    ' blit out upper tiles
    If NumTileSets > 0 Then
        For X = TileView.Left To TileView.Right
            For Y = TileView.Top To TileView.Bottom
                If IsValidMapPoint(X, Y) Then
                    Call BltMapFringeTile(X, Y)
                End If
            Next
        Next
    End If
    
    ' blit out a square at mouse cursor
    If InMapEditor Then
        If frmEditor_Map.optBlock.Value = True Then
            For X = TileView.Left To TileView.Right
                For Y = TileView.Top To TileView.Bottom
                    If IsValidMapPoint(X, Y) Then
                        Call BltDirection(X, Y)
                    End If
                Next
            Next
        End If
        Call BltTileOutline
    End If
    
    BltBars

    ' Lock the backbuffer so we can draw text and names
    TexthDC = DDS_BackBuffer.GetDC

    ' draw FPS
    If BFPS Then
        Call DrawText(TexthDC, Camera.Right - (Len("FPS: " & GameFPS) * 8), Camera.Top + 1, Trim$("FPS: " & GameFPS), QBColor(Yellow))
    End If

    ' draw cursor, player X and Y locations
    If BLoc Then
        Call DrawText(TexthDC, Camera.Left, Camera.Top + 1, Trim$("cur x: " & CurX & " y: " & CurY), QBColor(Yellow))
        Call DrawText(TexthDC, Camera.Left, Camera.Top + 15, Trim$("loc x: " & GetPlayerX(MyIndex) & " y: " & GetPlayerY(MyIndex)), QBColor(Yellow))
        Call DrawText(TexthDC, Camera.Left, Camera.Top + 27, Trim$(" (map #" & GetPlayerMap(MyIndex) & ")"), QBColor(Yellow))
    End If

    frmMainGame.lblFPS.Caption = GameFPS

    ' draw player names
    For i = 1 To MAX_PLAYERS
        If IsPlaying(i) And GetPlayerMap(i) = GetPlayerMap(MyIndex) Then
            Call DrawPlayerName(i)
        End If
    Next
    
    For i = 1 To MAX_BYTE
        Call BltActionMsg(i)
    Next i

    ' Blit out map attributes
    If InMapEditor Then
        Call BltMapAttributes
    End If

    ' Draw map name
    Call DrawText(TexthDC, DrawMapNameX, DrawMapNameY, Map.Name, DrawMapNameColor)

Continue:
    ' Release DC
    Call DDS_BackBuffer.ReleaseDC(TexthDC)
    ' Get the rect to blit to
    Call DX7.GetWindowRect(frmMainGame.picScreen.hWnd, rec_pos)
    ' Blit the backbuffer
    Call DDS_Primary.Blt(rec_pos, DDS_BackBuffer, Camera, DDBLT_WAIT)
    Exit Sub
ErrorHandle:

    If Err.Number = 91 Then
        Sleep 100
        Call ReInitDD
        Err.Clear
        Exit Sub
    End If

    On Error Resume Next

    If Not CheckSurfaces Then Exit Sub ' surfaces can get lost, check again
    TexthDC = DDS_BackBuffer.GetDC ' Lock the backbuffer so we can draw text and names
    Call DrawText(TexthDC, ConvertMapX(10), ConvertMapY(15), "Error Rendering Graphics - Unhandled Error", QBColor(BrightRed))
    Call DrawText(TexthDC, ConvertMapX(10), ConvertMapY(28), "Error Number : " & Err.Number & " - " & Err.Description, QBColor(BrightCyan))
    GoTo Continue
End Sub

Public Sub UpdateCamera()
    Dim offsetX As Long
    Dim offsetY As Long
    Dim StartX As Long
    Dim StartY As Long
    Dim EndX As Long
    Dim EndY As Long
    offsetX = Player(MyIndex).XOffset + PIC_X
    offsetY = Player(MyIndex).YOffset + PIC_Y
    StartX = GetPlayerX(MyIndex) - ((MAX_MAPX + 1) \ 2) - 1
    StartY = GetPlayerY(MyIndex) - ((MAX_MAPY + 1) \ 2) - 1

    If StartX < 0 Then
        offsetX = 0

        If StartX = -1 Then
            If Player(MyIndex).XOffset > 0 Then
                offsetX = Player(MyIndex).XOffset
            End If
        End If

        StartX = 0
    End If

    If StartY < 0 Then
        offsetY = 0

        If StartY = -1 Then
            If Player(MyIndex).YOffset > 0 Then
                offsetY = Player(MyIndex).YOffset
            End If
        End If

        StartY = 0
    End If

    EndX = StartX + (MAX_MAPX + 1) + 1
    EndY = StartY + (MAX_MAPY + 1) + 1

    If EndX > Map.MaxX Then
        offsetX = 32

        If EndX = Map.MaxX + 1 Then
            If Player(MyIndex).XOffset < 0 Then
                offsetX = Player(MyIndex).XOffset + PIC_X
            End If
        End If

        EndX = Map.MaxX
        StartX = EndX - MAX_MAPX - 1
    End If

    If EndY > Map.MaxY Then
        offsetY = 32

        If EndY = Map.MaxY + 1 Then
            If Player(MyIndex).YOffset < 0 Then
                offsetY = Player(MyIndex).YOffset + PIC_Y
            End If
        End If

        EndY = Map.MaxY
        StartY = EndY - MAX_MAPY - 1
    End If

    With TileView
        .Top = StartY
        .Bottom = EndY
        .Left = StartX
        .Right = EndX
    End With

    With Camera
        .Top = offsetY
        .Bottom = .Top + ScreenY
        .Left = offsetX
        .Right = .Left + ScreenX
    End With
    
    UpdateDrawMapName

End Sub

Public Function ConvertMapX(ByVal X As Long) As Long
    ConvertMapX = X - (TileView.Left * PIC_X)
End Function

Public Function ConvertMapY(ByVal Y As Long) As Long
    ConvertMapY = Y - (TileView.Top * PIC_Y)
End Function

Public Function InViewPort(ByVal X As Long, ByVal Y As Long) As Boolean
    InViewPort = False

    If X < TileView.Left Then Exit Function
    If Y < TileView.Top Then Exit Function
    If X > TileView.Right Then Exit Function
    If Y > TileView.Bottom Then Exit Function
    InViewPort = True
End Function

Public Function IsValidMapPoint(ByVal X As Long, ByVal Y As Long) As Boolean
    IsValidMapPoint = False

    If X < 0 Then Exit Function
    If Y < 0 Then Exit Function
    If X > Map.MaxX Then Exit Function
    If Y > Map.MaxY Then Exit Function
    IsValidMapPoint = True
End Function

Public Sub LoadTilesets()
    Dim X As Long
    Dim Y As Long
    Dim i As Long
    Dim tilesetInUse() As Boolean
    
    ReDim tilesetInUse(0 To NumTileSets)
    
    For X = 0 To Map.MaxX
        For Y = 0 To Map.MaxY
            For i = 1 To MapLayer.Layer_Count - 1
                tilesetInUse(Map.Tile(X, Y).Layer(i).tileset) = True
            Next
        Next
    Next
    
    For i = 1 To NumTileSets
        If tilesetInUse(i) Then
            ' load tileset
            If DDS_Tileset(i) Is Nothing Then
                Call InitDDSurf("tilesets\" & i, DDSD_Tileset(i), DDS_Tileset(i))
            End If
        Else
            ' unload tileset
            Call ZeroMemory(ByVal VarPtr(DDSD_Tileset(i)), LenB(DDSD_Tileset(i)))
            Set DDS_Tileset(i) = Nothing
        End If
    Next
End Sub

Sub BltBank()
Dim i As Long, X As Long, Y As Long, itemnum As Long
Dim Amount As String
Dim sRECT As RECT, dRECT As RECT
Dim Sprite As Long, colour As Long

    If frmMainGame.picBank.Visible = True Then
        frmMainGame.picBank.Cls
                
        For i = 1 To MAX_BANK
            itemnum = GetBankItemNum(i)
            If itemnum > 0 And itemnum <= MAX_ITEMS Then
            
                Sprite = Item(itemnum).Pic
                
                If DDS_Item(Sprite) Is Nothing Then
                    Call InitDDSurf("Items\" & Sprite, DDSD_Item(Sprite), DDS_Item(Sprite))
                End If
            
                With sRECT
                    .Top = 0
                    .Bottom = .Top + PIC_Y
                    .Left = DDSD_Item(Sprite).lWidth / 2
                    .Right = .Left + PIC_X
                End With
                
                With dRECT
                    .Top = BankTop + ((BankOffsetY + 32) * ((i - 1) \ BankColumns))
                    .Bottom = .Top + PIC_Y
                    .Left = BankLeft + ((BankOffsetX + 32) * (((i - 1) Mod BankColumns)))
                    .Right = .Left + PIC_X
                End With
                
                Engine_BltToDC DDS_Item(Sprite), sRECT, dRECT, frmMainGame.picBank, False

                ' If item is a stack - draw the amount you have
                If GetBankItemValue(i) > 1 Then
                    Y = dRECT.Top + 22
                    X = dRECT.Left - 4
                
                    Amount = CStr(GetBankItemValue(i))
                    ' Draw currency but with k, m, b etc. using a convertion function
                    If CLng(Amount) < 1000000 Then
                        colour = QBColor(White)
                    ElseIf CLng(Amount) > 1000000 And CLng(Amount) < 10000000 Then
                        colour = QBColor(Yellow)
                    ElseIf CLng(Amount) > 10000000 Then
                        colour = QBColor(BrightGreen)
                    End If
                    DrawText frmMainGame.picBank.hDC, X, Y, ConvertCurrency(Amount), colour
                End If
            End If
        Next
    
        frmMainGame.picBank.Refresh
    End If
End Sub

Public Sub BltBankItem(ByVal X As Long, ByVal Y As Long)
Dim sRECT As RECT, dRECT As RECT
Dim itemnum As Long
Dim Sprite As Long
    
    itemnum = GetBankItemNum(DragBankSlotNum)
    Sprite = Item(GetBankItemNum(DragBankSlotNum)).Pic
    
    If DDS_Item(Sprite) Is Nothing Then
        Call InitDDSurf("Items\" & Sprite, DDSD_Item(Sprite), DDS_Item(Sprite))
    End If
    
    If itemnum > 0 Then
        If itemnum <= MAX_ITEMS Then
            With sRECT
                .Top = 0
                .Bottom = .Top + PIC_Y
                .Left = DDSD_Item(Sprite).lWidth / 2
                .Right = .Left + PIC_X
            End With
        End If
    End If
    
    With dRECT
        .Top = 0
        .Bottom = .Top + PIC_Y
        .Left = 0
        .Right = .Left + PIC_X
    End With

    Engine_BltToDC DDS_Item(Sprite), sRECT, dRECT, frmMainGame.picTempBank
    
    With frmMainGame.picTempBank
        .Top = Y
        .Left = X
        .Visible = True
        .ZOrder (0)
    End With
    
End Sub
